package com.XZDP.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.XZDP.utils.Constant.RedisConstant;
import com.XZDP.utils.UserHolder;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.XZDP.dto.LoginFormDTO;
import com.XZDP.dto.Result;
import com.XZDP.entity.User;
import com.XZDP.mapper.UserMapper;
import com.XZDP.service.IUserService;
import com.XZDP.utils.MyUtils;
import com.XZDP.utils.RegexUtils;
import com.XZDP.utils.Constant.SystemConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import javax.servlet.http.HttpSession;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;


@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    private JavaMailSenderImpl mailSender;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Value("${spring.mail.username}")
    private String emailUsername;
    @Autowired
    private UserMapper userMapper;

    /**
     * 实现邮箱验证码登录业务
     *
     * @param phone
     * @param session
     * @return
     */
    @Override
    public Result email(String phone, HttpSession session) throws MessagingException {
        //验证邮箱格式
        log.info(String.valueOf(RegexUtils.isEmailInvalid(phone)));
        if (!RegexUtils.isEmailInvalid(phone)) {
            return Result.fail("邮箱不正确请重新输入");
        }
        //生成验证码
        String code = RandomUtil.randomNumbers(6);
        //TODO 之后通过MQ来优化邮件发送业务提高速度
        //通过邮件发送
        MimeMessage mimeMessage = mailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
        helper.setText("<p style='color: blue'>你的验证码为：" + code + "(有效期为一分钟)</p>", true);
        //设置邮件主题名
        helper.setSubject("蒋笑杰发你的验证码");
        //发给谁->邮箱地址
        helper.setTo(phone);
        //谁发的->发送人邮箱
        helper.setFrom(emailUsername);
        mailSender.send(mimeMessage);
        //将验证码存储到redis并设置一份钟过期时间
        redisTemplate.opsForValue().set("code:" + phone, code, Duration.ofMinutes(2));
        return Result.ok();
    }

    /**
     * 登录逻辑
     *
     * @param loginForm
     * @param session
     * @return
     */
    @Override
    public Result EquCode(LoginFormDTO loginForm, HttpSession session) {
        MyUtils myUtils = new MyUtils();
        //验证邮箱格式
        log.info(String.valueOf(RegexUtils.isEmailInvalid(loginForm.getPhone())));
        if (!RegexUtils.isEmailInvalid(loginForm.getPhone())) {
            return Result.fail("邮箱不正确请重新输入");
        }
        //数据库查询数据根据email查询用户信息
        User user = userMapper.getByEmail(loginForm);
        log.info(String.valueOf(user));
        if (loginForm.getCode() == null) {
            if (loginForm.getPhone() == null || loginForm.getPassword() == null) {
                return Result.fail("邮箱或密码不能为空");
            }
            //用户通过账号密码登录的  判断账号是否存在
            if (user == null) {
                //账号不存在的
                return Result.fail("该用户不存在,请前往注册");
            }
            //判断密码是否正确
            if (loginForm.getPassword().equals(user.getPassword())) {
                //相等
                String token = myUtils.TokenAndToRedis(user, redisTemplate);
                return Result.ok(token);
            } else {
                return Result.fail("密码错误");
            }
        }
        //通过验证码和账号登录的
        //先处理验证码，在考虑是否为新用户
        //校验验证码格式
        if (!RegexUtils.isCodeInvalid(loginForm.getCode())) {
            //验证码格式错误
            return Result.fail("验证码格式错误");
        }
        //先判断redis是否存在数据
        boolean type = redisTemplate.hasKey("code:" + loginForm.getPhone());
        //从redis里读取验证码
        if (type) {
            //存在key
            String code = (String) redisTemplate.opsForValue().get("code:" + loginForm.getPhone());
            if (!code.equals(loginForm.getCode())) {
                return Result.fail("验证码错误");
            }
            if (user == null) {
                user = new User();
                user.setEmail(loginForm.getPhone());
                //设置随机名字
                user.setNickName(SystemConstants.USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
                user.setCreateTime(LocalDateTime.now());
                user.setUpdateTime(LocalDateTime.now());
                //为其注册账号
                userMapper.addUser(user);
            }
        } else {
            return Result.fail("验证码不存在");
        }
        String token = myUtils.TokenAndToRedis(user, redisTemplate);
        //将token返回前端存储
        return Result.ok(token);
    }

    /**
     * 用户签到
     *
     * @return
     */
    public Result sign() {
        //获取用户ID
        Long id = UserHolder.getUser().getId();
        //获取当前时间
        LocalDateTime now = LocalDateTime.now();
        //时间前缀
        String keysuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        //存入redis
        redisTemplate.opsForValue().setBit(RedisConstant.SIGN_KEY + id + keysuffix, now.getDayOfMonth() - 1, true);
        return Result.ok();
    }

    /**
     * 用户连续签到
     *
     * @return
     */
    @Override
    public Result countSign() {
        //获取用户ID
        Long id = UserHolder.getUser().getId();
        //获取当前时间
        LocalDateTime now = LocalDateTime.now();
        //获取今天是这个月第几天
        int day = now.getDayOfMonth();
        String keysuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key = RedisConstant.SIGN_KEY + id + keysuffix;
        //获取签到信息
        List<Long> result = redisTemplate.opsForValue().bitField(
                key,
                BitFieldSubCommands.create().
                        get(BitFieldSubCommands.BitFieldType.unsigned(day)).valueAt(0));
        //判断是否为空
        if (result.isEmpty() || result == null) {
            return Result.ok("无签到记录");
        }
        Long bit = result.get(0);
        int count = 0;
        //循环遍历进行与运算
        while (true) {
            if ((1 & bit) == 1) {
                //计数器+1
                count++;
            } else {
                break;
            }
            //位移
            bit = bit >>> 1;
        }
        return Result.ok(count);
    }
}
